﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace Ict.DataAccess.Linq
{
    /// <summary>
    /// 同时查询两张表的查询
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class SelectableMany<T> : Selectable<T>
    {
        private SelectableBase sourceQuery1;
        private SelectableBase sourceQuery2;


        internal SelectableMany(SelectableBase sourceQuery1, SelectableBase sourceQuery2,
            LambdaExpression selectClause)
        {
            ((ExecutableProvider)this.Provider).Executor = ((ExecutableProvider)sourceQuery1.Provider).Executor;

            this.sourceQuery1 = sourceQuery1;
            this.sourceQuery2 = sourceQuery2;

            this.SelectClause = selectClause;

            this.sourceQuery1.Alias = selectClause.Parameters[0].Name;
            this.sourceQuery2.Alias = selectClause.Parameters[1].Name;
        }

        internal SelectableBase SourceQuery1
        {
            get
            {
                return sourceQuery1;
            }
        }

        internal SelectableBase SourceQuery2
        {
            get
            {
                return sourceQuery2;
            }
        }

        internal override string MapField(MemberExpression memberAccess)
        {
            System.Reflection.MemberInfo member = memberAccess.Member;
            Type type = member.DeclaringType;
            if (MapFieldByAlias(memberAccess, sourceQuery1.Alias))
                return sourceQuery1.MapField(memberAccess);
            if (MapFieldByAlias(memberAccess, sourceQuery2.Alias))
                return sourceQuery2.MapField(memberAccess);
            return null;
        }

        private bool MapFieldByAlias(MemberExpression memberAccess, string alias)
        {
            while (true)
            {
                if (memberAccess.Member.Name == alias)
                    return true;

                if (memberAccess.Expression is MemberExpression)
                {
                    memberAccess = memberAccess.Expression as MemberExpression;
                }
                else
                {
                    if (memberAccess.Expression is ParameterExpression)
                    {
                        if ((memberAccess.Expression as ParameterExpression).Name == alias)
                            return true;
                    }
                    break;
                }
            }
            
            return false;
        }

        internal override void BuildFieldList(StringBuilder stringBuilder)
        {
            BuildFieldList(stringBuilder, this.SelectClause.Body);
        }

        internal override void BuildFieldList(StringBuilder stringBuilder, Expression expression)
        {
            if (expression.NodeType == ExpressionType.MemberAccess)
            {
                if ((expression as MemberExpression).Member.DeclaringType == sourceQuery1.ElementType)
                    sourceQuery1.BuildFieldList(stringBuilder, expression);
                else if ((expression as MemberExpression).Type == sourceQuery1.ElementType)
                    sourceQuery1.BuildFieldList(stringBuilder);
                else if ((expression as MemberExpression).Member.DeclaringType == sourceQuery2.ElementType)
                    sourceQuery2.BuildFieldList(stringBuilder, expression);
                else if ((expression as MemberExpression).Type == sourceQuery2.ElementType)
                    sourceQuery2.BuildFieldList(stringBuilder);
            }
            else if (expression.NodeType == ExpressionType.Parameter)
            {
                if (expression.Type == sourceQuery1.ElementType)
                    sourceQuery1.BuildFieldList(stringBuilder);
                else if (expression.Type == sourceQuery2.ElementType)
                    sourceQuery2.BuildFieldList(stringBuilder);
            }
            else
                base.BuildFieldList(stringBuilder, expression);
        }

        internal override void BuildQueryList(StringBuilder stringBuilder)
        {
            BuildSubQuery(stringBuilder, sourceQuery1);
            stringBuilder.Append(',');
            BuildSubQuery(stringBuilder, sourceQuery2);
        }
    }

    /// <summary>
    /// 连接两张表的查询
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class SelectableJoin<T> : SelectableMany<T>
    {
        private Expression joinField1;
        private Expression joinField2;

        internal SelectableJoin(SelectableBase sourceQuery1, SelectableBase sourceQuery2,
            Expression joinField1, Expression joinField2, LambdaExpression selectClause)
            : base(sourceQuery1, sourceQuery2, selectClause)
        {
            this.joinField1 = joinField1;
            this.joinField2 = joinField2;
        }

        internal Expression JoinField1
        {
            get
            {
                return joinField1;
            }
        }

        internal Expression JoinField2
        {
            get
            {
                return joinField2;
            }
        }

        internal override void BuildQueryList(StringBuilder stringBuilder)
        {
            BuildSubQuery(stringBuilder, SourceQuery1);
            stringBuilder.Append(" JOIN ");
            BuildSubQuery(stringBuilder, SourceQuery2);
            stringBuilder.Append(" ON ");

            BuildJoinOnCondition(stringBuilder, joinField1, joinField2);
        }

        private void BuildJoinOnCondition(StringBuilder stringBuilder, System.Linq.Expressions.Expression joinField1, System.Linq.Expressions.Expression joinField2)
        {
            if (joinField1 is NewExpression)
            {
                IEnumerator<Expression> ijoin1 = (joinField1 as NewExpression).Arguments.GetEnumerator();
                IEnumerator<Expression> ijoin2 = (joinField2 as NewExpression).Arguments.GetEnumerator();

                if (ijoin1.MoveNext())
                {
                    ijoin2.MoveNext();
                    do
                    {
                        BuildJoinOnCondition(stringBuilder, ijoin1.Current, ijoin2.Current);
                        if (!ijoin1.MoveNext())
                            break;
                        ijoin2.MoveNext();
                        stringBuilder.Append(" AND ");
                    } while (true);
                }
            }
            else
            {
                if (joinField1 is UnaryExpression && joinField1.NodeType== ExpressionType.Convert)
                {
                    joinField1 = (joinField1 as UnaryExpression).Operand;
                }
                if (joinField2 is UnaryExpression && joinField2.NodeType == ExpressionType.Convert)
                {
                    joinField2 = (joinField2 as UnaryExpression).Operand;
                }
                stringBuilder.Append(MapField((MemberExpression)joinField1));
                stringBuilder.Append('=');
                stringBuilder.Append(MapField((MemberExpression)joinField2));
            }
        }
    }
}
